"use strict";

var router = require("router"),
    TokenService = require("router").TokenService,
    mongo = require('./core/mongoservice'),
    ObjectId = require('mongodb').ObjectID,
    Q = require('q'),
    gridFs = require('./gridfsservice'),
    Jimp = require("jimp");

function ImageService() {
    this.reData = /data:(\S*);(\S*),(.+)/;
    this.reApi = /\/api\/image\/(\S|\/)+/;
}
ImageService.prototype = Object.create(TokenService.prototype);
ImageService.prototype.constructor = ImageService;

/*--------------------------------------------*/
/* selectors*/ 

/* w600h400 => { width:400, height:600} */
ImageService.prototype.splitMedia = function (media) {
    return Q.fcall(function(){
        var width = 0;
        var height = 0;

        if(media){
            var tokens = media.split("h");
            if(tokens.length > 0) width = tokens[0].slice(1);
            if(tokens.length > 1) height = tokens[1];
        }

        return {width: width, height: height};
    });
};


ImageService.prototype.getShopFront = function (request, response, promise) {
    return promise.then(function (self) {

        var imageId = request.params.id;
        var media = request.params.media;

        return self.splitMedia(media).then(function (md) {
            return Q.fcall(function(){
                return self.mongoGetById(imageId).then(function (image) {

                    if (md.width <= 480) return gridFs.get(image.w480h240.id);
                    if (md.width <= 600) return gridFs.get(image.w600h300.id);
                    return gridFs.get(image.w800h400.id).then(function (data) {
                        return response.send({ request: request, content: data.image, header: { 'Content-Type': data.contentType} });
                    });
                });
            }).catch(function(){
                if (md.width <= 480) request.url = "/assets/images/shop/w480h240.jpg";
                else if (md.width <= 600) request.url = "/assets/images/shop/w600h300.jpg";
                else request.url = "/assets/images/shop/w800h400.jpg";
                router.assets.invoke(request, response);
            });
        });
    });
};

ImageService.prototype.saveShopFront = function (request, response, promise) {
    return promise.then(function (self) {

        var match = self.reData.exec(request.body.datauri);
        if (!match) {
            if(self.reApi.exec(request.body.datauri)) return {src: request.body.datauri};
            else throw new Error("Incompatible datauri.");
        }

        var contentType = match[1];
        var data64 = match[3];
        var sizes = [{width: 800, height: 400}, {width: 600, height: 300}, {width: 480, height: 240}];

        return self.mongoSaveDataUri(contentType, sizes, data64).then(function (res) {
            return self.mongoSave(res).then(function(data){
                return {src: "/api/image/shop/front/" + data._id};
            });
        });

    }).then(function (data) {
        return response.send({ request: request, content: data });
    });
};

ImageService.prototype.getShopPicto = function (request, response, promise) {
    return promise.then(function (self) {

        var imageId = request.params.id;

        return self.mongoGetById(imageId).then(function (image) {
            return gridFs.get(image.w48h48.id).then(function (data) {
                return response.send({ request: request, content: data.image, header: { 'Content-Type': data.contentType} });
            });
        });
    }).catch(function(){
        request.url = "/assets/images/shop/picto.png";
        router.assets.invoke(request, response);
    });
};

ImageService.prototype.saveShopPicto = function (request, response, promise) {
    return promise.then(function (self) {

        var match = self.reData.exec(request.body.datauri);
        if (!match) {
            if(self.reApi.exec(request.body.datauri)) return {src: request.body.datauri};
            else throw new Error("Incompatible datauri.");
        }

        var contentType = match[1];
        var data64 = match[3];
        var sizes = [{width: 48, height: 48}];

        return self.mongoSaveDataUri(contentType, sizes, data64).then(function (res) {
            return self.mongoSave(res).then(function(data){
                return {src: "/api/image/shop/picto/" + data._id};
            });
        });

    }).then(function (data) {
        return response.send({ request: request, content: data });
    });
};

ImageService.prototype.getUserPicto = function (request, response, promise) {
    return promise.then(function (self) {

        var imageId = request.params.id;

        return self.mongoGetById(imageId).then(function (image) {
            return gridFs.get(image.w48h48.id).then(function (data) {
                return response.send({ request: request, content: data.image, header: { 'Content-Type': data.contentType} });
            });
        });
    }).catch(function(){
        request.url = "/assets/images/user/picto.png";
        router.assets.invoke(request, response);
    });
};

ImageService.prototype.saveUserPicto = function (request, response, promise) {
    return promise.then(function (self) {

        var match = self.reData.exec(request.body.datauri);
        if (!match) {
            if(self.reApi.exec(request.body.datauri)) return {src: request.body.datauri};
            else throw new Error("Incompatible datauri.");
        }

        var contentType = match[1];
        var data64 = match[3];
        var sizes = [{width: 48, height: 48}];

        return self.mongoSaveDataUri(contentType, sizes, data64).then(function (res) {
            return self.mongoSave(res).then(function(data){
                return {src: "/api/image/user/picto/" + data._id};
            });
        });

    }).then(function (data) {
        return response.send({ request: request, content: data });
    });
};

ImageService.prototype.getAlertImage = function (request, response, promise) {
    return promise.then(function (self) {

        var imageId = request.params.id;
        var media = request.params.media;

        return self.splitMedia(media).then(function (md) {
            return Q.fcall(function(){
                return self.mongoGetById(imageId).then(function (image) {
                    if (md.width <= 480) return gridFs.get(image.w480h240.id);
                    if (md.width <= 600) return gridFs.get(image.w600h300.id);
                    return gridFs.get(image.w800h400.id);
                }).then(function (data) {

                    return response.send({ request: request, content: data.image, header: { 'Content-Type': data.contentType} });
                });
            }).catch(function(err){
                if (md.width <= 480) request.url = "/assets/images/alert/w480h240.jpg";
                else if (md.width <= 600) request.url = "/assets/images/alert/w600h300.jpg";
                else request.url = "/assets/images/alert/w800h400.jpg";
                router.assets.invoke(request, response);
            });
        });
    });
};

ImageService.prototype.saveAlertImage = function (request, response, promise) {
    return promise.then(function (self) {

        var match = self.reData.exec(request.body.datauri);
        if (!match) {
            if(self.reApi.exec(request.body.datauri)) return {src: request.body.datauri};
            else throw new Error("Incompatible datauri.");
        }

        var contentType = match[1];
        var data64 = match[3];
        var sizes = [{width: 800, height: 400}, {width: 600, height: 300}, {width: 480, height: 240}];

        return self.mongoSaveDataUri(contentType, sizes, data64).then(function (res) {
            return self.mongoSave(res).then(function(data){
                return {src: "/api/image/alert/image/" + data._id};
            });
        });

    }).then(function (data) {
        return response.send({ request: request, content: data });
    });
};


/*------------------------------*/

ImageService.prototype.mongoSaveDataUri = function (contentType, sizes, data64) {
    if (!contentType) throw new Error("ContentType is not defined.");
    if (!sizes) throw new Error("Sizes is not defined.");
    if (!data64) throw new Error("Data64 is not defined.");
    var self = this;

    var buffer = new Buffer(data64, 'base64');

    var promises = [];
    sizes.forEach(function (size) {
        var promise = self.toImage(buffer, contentType).then(function (image) {        
            return self.transform(image, size).then(function (newImage) {
                return self.toBuffer(newImage, "image/jpeg").then(function (newBuffer) {
                    return self.size(newImage).then(function (newSize) {
                        return gridFs.save("image/jpeg", newBuffer).then(function (data) { /*image format in mongo: jpeg*/
                            console.log("save in gridf. size", JSON.stringify(size), "width",newSize.width);
                            return {
                                id: data.id,
                                width: newSize.width,
                                height: newSize.height,
                                property: "w" + newSize.width + "h" + newSize.height
                            };
                        });
                    });
                });
            });
        });
        promises.push(promise);

    });

    return Q.all(promises).then(function(data){
        return data.reduce(function (previous, current) {
            previous[current.property] = {
                id: current.id,
                width: current.width,
                height: current.height
            };
            return previous;
        }, {});
    }).then(function(data){
        console.log("mongoSaveDataUri after reduce", JSON.stringify(data));
        return data;
    });
};

/*-------------------------------------------------------*/
/*common*/

ImageService.prototype.toImage = function (buffer, contentType) {

    var deferred = Q.defer();
    new Jimp(buffer, contentType, function () {
        deferred.resolve(this);
    });

    return deferred.promise;
}

ImageService.prototype.toBuffer = function (image, contentType) {
    if (!contentType) throw new Error("contentType is not defined.");

    var deferred = Q.defer();
    image.getBuffer(contentType, function (buffer) {
        deferred.resolve(buffer);
    });

    return deferred.promise;
}

ImageService.prototype.size = function (image) {
    return Q.fcall(function () {
        var size = { width: image.bitmap.width, height: image.bitmap.height };
        return size;
    });
};

ImageService.prototype.resize = function (image, width, height) {
    if (!width) throw new Error("width is not defined.");
    if (!height) throw new Error("height is not defined.");
    var self = this;

    return Q.fcall(function () {
        image.resize(width, height);
        return image;
    });
}

ImageService.prototype.crop = function (image, left, top, width, height) {
    if (left == null) throw new Error("left is not defined.");
    if (top == null) throw new Error("top is not defined.");
    if (width == null) throw new Error("width is not defined.");
    if (height == null) throw new Error("height is not defined.");

    return Q.fcall(function () {
        image.crop(left, top, width, height)
        return image;
    });
}

ImageService.prototype.smartResize = function (image, width, height) {
    if (!image) throw new Error("image is not defined.");
    if (!width) throw new Error("width is not defined.");
    if (!height) throw new Error("height is not defined.");
    var self = this;
    return self.size(image).then(function (size) {
        var cropRatio = width / height;
        var imageRatio = size.width / size.height;
        if (cropRatio == imageRatio) {
            return self.resize(image, width, height);
        }
        if (cropRatio < imageRatio) {
            var resizedWidth = height * imageRatio;
            return self.resize(image, resizedWidth, height).then(function (resizedImage) {
                return self.size(resizedImage).then(function (resized) {
                    var left = (resized.width - width) / 2;
                    return self.crop(resizedImage, left, 0, width, height);  
                });
            });
        }
        else {
            var resizedHeight = width / imageRatio;
            
            return self.resize(image, width, height).then(function (resizedImage) {
                return self.size(resizedImage).then(function (resized) {
                    var top = (resized.height - height) / 2;
                    return self.crop(resizedImage, 0, top, width, height);  
                });
            });
        }
    });
}

ImageService.prototype.transform = function (image, size) {
    return this.smartResize(image, size.width, size.height);
}

/*-------------------------------------------------------*/
/*mongo daf*/

ImageService.prototype.mongoSave = function (image) {
    if (!image) throw new Error("image is not defined.");
    var self = this;

    if (image._id) return self.mongoUpdate(image);
    else return self.mongoInsert(image); 
}

ImageService.prototype.mongoInsert = function (image) {
    if (!image) throw new Error("image is not defined.");
    if (image._id) throw new Error("image already has id: " + image._id);
    var self = this;

    return mongo.insert('images', image).then(function (images) {
        return images[0];
    }).then(function(image){
        return self.mongoGetById(image._id);
    });
};

ImageService.prototype.mongoUpdate = function (image) {
    if (!image) throw new Error("image is not defined.");
    var self = this;

    return self.mongoGetById(image._id).then(function (image) {
        return mongo.update('images', { _id: image._id }, image).then(function (res) {
            return self.mongoGetById(image._id);
        });
    });
};

ImageService.prototype.mongoGetById = function (imageId) {
    if (!imageId) throw new Error("imageId is not defined.");
    if (imageId == "default") throw new Error("imageId is not defined.");

    return mongo.findOne('images', { _id: new ObjectId(imageId) });
};

exports = module.exports = new ImageService();